/*
Copyright (c) 2013 Mihail Volkov

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef HTMD_H_INCLUDED
#define HTMD_H_INCLUDED

// Unrem next line (or add a macro to compiler options) to
// disable HtMd. For robustness, its definition excludes any
// HtMd code and declarations completely, in order to enforce
// your own HtMd logic code to check for this macro (if you
// forget to to it, the compiler will report error on any
// HtMd references).
//#define DISABLE_HTMD

#ifndef DISABLE_HTMD

// Client<->app communication stream is split into atomic packets
// each of which represents an individual low-level command.
// Packet into either side must not exceed a certain length. For
// the app, this length is specified in HTMD_MAX_PACKET_SIZE.
// For the client, it will be adjusted in the beginning of the
// session via app_hello command.
#define HTMD_MAX_PACKET_SIZE 512

// up to how much bytes can be pending in the buffer
// when downloading files (to client), somewhat limits bandwidth
#define HTMD_MAX_DNLD_FILE_PENDING_BYTES 4096

// major protocol version number, bumps when backwards protocol
// compatibility with previous version is fully or partially lost
#define HTMD_PROTO_VER_MAJOR 1

// major protocol version number, bumps when backwards protocol
// compatibility with previous version is fully retained
#define HTMD_PROTO_VER_MINOR 0

// major protocol version number, bumps when both backwards
// and forwards protocol compatibility with previous version
// is fully retained
#define HTMD_PROTO_VER_REVISION 0

#include <string>
#include <deque>
#include <map>
#include <set>

class HtMdConnection;
class HtMdConstChunkDesc;
class HtMdChunkDesc;
class HtMdUnit;
class HtMdSupervisor;
class HtMdLowLevelAppCmd; // used internally, you don't need it

/// @brief A safe(ish) wrapper around a flat read-only buffer of char-s
/// of fixed length.
class HtMdConstChunkDesc
{
	friend class HtMdChunkDesc;
	char *mpBuf;
	int mLen;

public:
	/// @brief Construct a descriptor of the buffer len bytes long
	/// starting at pBuf
	HtMdConstChunkDesc (const char *pBuf,int len):
		mpBuf ((char *)pBuf), mLen (len) {}
	/// @brief Get pointer to the buffer's const data
	operator const char * () const { return mpBuf; }
	/// @brief Const access to character under a specific index
	char operator [] (int index) const;
	/// @brief Get length of the buffer
	int getLength () const { return mLen; }
	/// @brief Get a non-modifiable subshunk
	HtMdConstChunkDesc ConstSubchunk (int startAt,int len=-1) const;
};

/// @brief A safe(ish) wrapper around a flat read-write buffer of
/// char-s of fixed length.
class HtMdChunkDesc: public HtMdConstChunkDesc
{
public:
	/// @brief Construct a descriptor of the buffer len bytes long
	/// starting at pBuf
	HtMdChunkDesc (char *pBuf,int len):
		HtMdConstChunkDesc (pBuf,len) {}
	/// @brief Get pointer to the buffer's data
	operator char * () { return mpBuf; }
	/// @brief Non-const access to character under a specific index
	char &operator [] (int index);
	/// @brief Get a subshunk
	HtMdChunkDesc Subchunk (int startAt,int len=-1);
};

/// @brief Connection interface for use by HtMd supervisor. One-client
/// server that starts listening as it's constructed, accepts
/// connection from the client and communicates, doing all this in
/// non-blocking way.
///
/// This class is an entirely abstract interface. You must make a
/// derived class that provides implementation of the interface over
/// a concrete channel, instantiate that class in your application
/// and use that instance when creating HtMdSupervisor (see below).
/// For sake of threading safety, the connection should also provide
/// an associated recursive mutex/semaphore/critical section and
/// functions to control it (tryRecursiveLock, recursiveUnlock and
/// recursiveUnlock). If multitreading is not an issue in your case,
/// you can just define them to empty stubs.
class HtMdConnection
{
public:
	/// @brief State of the connection
	enum ConnectionState
	{
		/// @brief The server is not working. This state can not be
		/// fixed, you can only try to create another instance.
		DEFUNCT,
		/// @brief The server is not connected to the client and is
		/// listening for incoming connection
		NOT_CONNECTED,
		/// @brief The server is not connected to the client
		CONNECTED
	};

	virtual ~HtMdConnection () {};

	/// @brief Returns user readable text of information needed by user
	/// to connect to this server (IP address and TCP port).
	virtual std::string getConnectionUserPrompt () = 0;

	/// @brief Returns connection state. NOT_CONNECTED if user has not
	/// connected (or after he has disconnected) and the connection is
	/// awaited, or CONNECTED if user is connected.
	/// By default, server should be in NOT_CONNECTED mode and listen
	/// for incoming client connections
	virtual ConnectionState getState () = 0;

	/// @brief Polls for incoming connection, accepts it and sets the
	/// state to CONNECTED if the connection is requested. Should do
	/// nothing if the state is already CONNECTED.
	virtual void pollForInboundConnection () = 0;

	/// @brief Closes incoming connection to client, resets the state
	/// to NOT_CONNECTED and resumes listening mode. Should do nothing
	/// if the state is already NOT_CONNECTED.
	virtual void closeInboundConnection () = 0;

	/// @brief Tries to receive up to bufSize bytes from client into
	/// buf, returns number of bytes actually read. 0 if failed to read
	/// or if no incoming data. If the connection is shutdown by the
	/// client, must return 0 and set the connection state to
	/// NOT_CONNECTED and resume listening mode.
	virtual int tryRead (HtMdChunkDesc buf) = 0;

	/// @brief Tries to send up to bufSize bytes to client from buf,
	/// returns number of bytes actually written. 0 if failed to write
	/// any bytes. If the connection is shutdown by the client, must
	/// return 0 and set the connection state to NOT_CONNECTED.
	virtual int tryWrite (const HtMdConstChunkDesc &data) = 0;

	/// @brief Try to lock associated mutex/CS. Return true if the
	/// lock is acquired, false if it is owned by another thread.
	/// In case of success, including the case when the mutex/CS
	/// is already owned by current thread, must return true and
	/// increment lock counter.
	virtual bool tryRecursiveLock () = 0;
	/// @brief Acquire associated mutex/CS, waiting for it to be freed
	/// by other thread if needed, and increment the lock counter. In
	/// case if the mutex/CS is already owned by current thread, must
	/// return immedietely and just increment the lock counter.
	virtual void recursiveLock () = 0;
	/// @brief Decrement lock counter and free the mutex/CS (assumed
	/// to be owned by calling thread) if it reaches 0. That is, in
	/// order to release the lock, every recursiveLock or successful
	/// tryRecursiveLock must be matched by corresponding
	/// recursiveUnlock.
	virtual void recursiveUnlock () = 0;
};

/// @brief HtMd unit. A unit is a source of outcoming commands (to
/// the console) and an acceptor of incoming commands (from it to your
/// app). Your class derived from HtMdUnit is expected to provide
/// a piece of debug interactivity relatively independent on others.
/// In Hatul Madan, each unit of the app has an associated window in
/// the client (a 'unit presentation').
class HtMdUnit
{
	friend class HtMdSupervisor;
	friend class HtMdLLCAppCmdBegin;
	friend class HtMdLLCAppCmdChunk;
	friend class HtMdLLCAppCmdCancel;
	friend class HtMdLLCAppWindowOpen;
	friend class HtMdLLCAppWindowClose;

	int mUnitId;
	bool mUnitWindowOpen,mClientTurnedIn;
	bool mClientCommandInProgress,mUnitCommandInProgress;
	int mUnitCommandLenSofar,mUnitCommandLenTotal;
	int mClientCommandLenSofar,mClientCommandLenTotal;

protected:
	HtMdUnit ();
	virtual ~HtMdUnit ();

	/// @brief Return true if client console is connected, false
	/// otherwise. A shortcut to HtMdSupervisor::isConnected.
	static bool isConnected ();

	/// @brief Callback from supervisor called when an input command
	/// for this unit is about to be sent. Command is a single string
	/// of char-s sent in parts, it's up to your unit class to
	/// determine how to accumulate, store and to interpret it.
	/// Won't be called again until inClientCommandOk or
	/// inConsoleCommandCancel are invoked, you can rely on this
	/// assumption.
	/// @param len Expected length of the command in bytes
	virtual void inClientCommandBegin (int len) = 0;

	/// @brief Callback from supervisor called when a chunk of data for
	/// this unit's current input command is arrived. These chunks,
	/// arranged in same order they were passed to the unit,
	/// concatenate into a complete command.
	/// inClientCommandChunk, form the complete input command string.
	/// Only will be called again between inClientCommandBegin and
	/// until inClientCommandOk or inConsoleCommandCancel are invoked,
	/// you can rely on this assumption.
	/// May be not called at all for zero-length command.
	/// @param chunk Descriptor of the data chunk
	virtual void inClientCommandChunk
		(const HtMdConstChunkDesc &chunk) = 0;

	/// @brief Callback from supervisor called when input command
	/// transmission has completed. Your unit is expected to check the
	/// resulting command string it accumulated during preceding series
	/// of inClientCommandChunk-s, to interpret it according on the
	/// unit's discretion and to perform the action. After that, the
	/// input command string is no longer needed and can be disposed.
	/// The action should not be blocking (that is, if execution of the
	/// command is a long background or an interactive operation, the
	/// inClientCommandOk should start it in some asynchronous way and
	/// return as soon as possible).
	/// Won't be called without previous inClientCommandBegin, you can
	/// rely on this assumption.
	virtual void inClientCommandOk () = 0;

	/// @brief Callback from supervisor called when input command
	/// transmission has been cancelled for any reason. Your unit is
	/// expected to discard whatever data it had accumulated via
	/// inClientCommandChunk-s and to prepare for next command. No
	/// actions on the undertransmitted command must be taken.
	/// Won't be called without previous inClientCommandBegin, you can
	/// rely on this assumption.
	virtual void inClientCommandCancelled () = 0;

	/// @brief Call this function when your unit has an output command
	/// to send to console. Note: if you do it until transmission of
	/// your previous output command completes (see
	/// outUnitCommandDone), that previous command will be treated as
	/// cancelled (as per outUintCommandCancel) and won't go to
	/// console.
	/// @param len Length of the command you are going to send.
	/// Supervisor will try to send exactly that number of bytes in
	/// total.
	void outUnitCommandBegin (int len);

	/// @brief Callback from supervisor requesting another chunk of
	/// unit's current output command to send. May be not called
	/// at all for zero-length command.
	/// @param chunk Descriptor of the data chunk to fill
	/// @return Number of bytes used
	virtual int outUnitCommandChunk (HtMdChunkDesc &chunk) = 0;

	/// @brief Call this function when your current output command
	/// should be cancelled for whatever reason. No more
	/// outUintCommandChunk-s will be called until next
	/// outUnitCommandBegin. Note: if outUnitCommandSent is called,
	/// it is late to cancel and the command is already sent to the
	/// console.
	/// Always safe to call, just has no effect if sending of output
	/// command is not in progress.
	void outUnitCommandCancel ();

	/// @brief Callback from supervisor called when the whole length
	/// you specified in outUnitCommandBegin has been sent to console
	/// and you can begin another output command.
	/// Won't be called between after outUnitCommandCancel and
	/// before next outUnitCommandBegin, but will always be called
	/// after outUnitCommandBegin if there was no cancel, you can rely
	/// on this assumption.
	virtual void outUnitCommandDone () = 0;

	/// @brief Callback from supervisor called when the client console
	/// is connected to the application (also if the unit object is
	/// created while the console is connected). The unit is supposed
	/// to do stuff to init its presentation in the client, including
	/// but possibly not limited to call outUnitOpenWindow, and to
	/// prepare whatever data it needs to service the interactivity.
	virtual void onClientConnected () = 0;

	/// @brief Callback from supervisor called when the client console
	/// is disconnected from the application. The unit is supposed to
	/// dispose data and shutdown activities that it doesn't need
	/// without client and to prepare to handle next onClientConnected.
	virtual void onClientDisconnected () = 0;

	/// @brief Callback from supervisor called when the unit's
	/// presentation window is ready to handle commands (it occurs when
	/// the presentation web page's js invokes htmdUnit.turnIn ()).
	/// Until this function is called, commands sent via
	/// outUnitCommandBegin/outUnitCommandChunk are not guaranteed to
	/// affect the client.
	/// Not called between onClientConnected and onClientDisconnected.
	virtual void onClientTurnIn () = 0;

	/// @brief Callback from supervisor called when the unit's
	/// presentation window's ability to recept commands is suspended
	/// (it occurs when the presentation web page is reloaded,
	/// navigated elsewhere, or its window is closed by user).
	/// After this function is called, commands sent via
	/// outUnitCommandBegin/outUnitCommandChunk (including one
	/// currently being outUnitCommandChunk-ed) are not guaranteed to
	/// affect the client (until next onClientTurnIn). The unit can
	/// use this callback to cancel and cleanup current sending.
	/// Not called between onClientConnected and onClientDisconnected.
	virtual void onClientSuspended () = 0;

	/// @brief Callback from supervisor called for each existing
	/// unit on every HtMdSupervisor::update, regardless on connection
	/// status. Default implementation does nothing.
	virtual void onUpdate () {}

	/// @brief Issue command to the console to open window for this
	/// unit's presentation and load document under url into it.
	/// Url htmd:/file_path refers to path relative to console kit
	/// location directory user has chosen in console's startup dialog,
	/// ass:/file_path refers to path relative to application assets.
	/// If the window is already open at the same url, a 'repoened'
	/// event will be sent to that window's htmdUnit object.
	/// Has no effect if the console is not connected.
	/// You should do it in every onClientConnected as a client has no
	/// windows open in the beginning of the session, and no output
	/// commands from the unit will have effect until there is an open
	/// window in the console to accept them.
	/// @note Length of url should not exceed HTMD_MAX_PACKET_SIZE-12,
	/// or it will be truncated.
	void outUnitOpenWindow (const std::string &url);

	/// @brief Issue command to the console to close window used for
	/// this unit's presentation and dispose associated resources.
	/// The window can be re-opened later by new outUintOpenWindow.
	/// Window is also closes automatically if the unit object is
	/// destructed.
	void outUnitCloseWindow ();
};

/// @brief HtMd file uploader (from client to application). You should
/// derive a class from HtMdFileUploader that implements storing data
/// accepted from the client into what is considered a file in your
/// particular implementation.
/// An instance of HtMdFileUploader incapsulates a session of uploading
/// a single file (request to the client, confirmation from the client
/// and the actual data). The uploader instance is assumed reusable,
/// but only can handle one request at a time.
class HtMdFileUploader
{
	friend class HtMdSupervisor;
	friend class HtMdLLCFileUpload;

	enum
	{
		ST_OK,
		ST_CANCELLED,
		ST_CLIENT_CANCEL_IN_PROGRESS,
		ST_QUEUED,
		ST_WAITING_ACK,
		ST_WAITING_DATA
	} mState;
	int mDataTotal,mDataSofar;
	std::string mSuggName;

protected:
	HtMdFileUploader ();
	virtual ~HtMdFileUploader ();

	/// @brief Request beginning of an upload, recommend name/filter
	/// for the file to upload. Using it while this upload is already
	/// in progress cancels current upload automatically.
	/// Your implementation is assumed provide its own function to
	/// begin the upload according to specifics of the situation
	/// (open a file for writing, or allocate a buffer for uploading
	/// into memory, etc.), which in turn must internally call
	/// beginRequestUpload to begin HtMd part of the work.
	/// @param nameOrFilter Suggested file name or filter, in Qt
	/// filters format. It will be present as filter in client's
	/// open file dialog.
	void beginRequestUpload (const std::string &nameOrFilter);

	/// @brief Client has confirmed beginning of the upload. Once
	/// called, is guaranteed not to be called again until next
	/// inUploadOk/inUploadCancelled.
	/// @param selectedName Name of the file in client's file system
	/// that user has selected for uploading (without path)
	/// @param size Size of the uploading file, in bytes
	virtual void inUploadBegin (const std::string &selectedName,int size) = 0;

	/// @brief A chunk of to append to the file being accepted. Total
	/// size of file chunks are guaranteed to not exceed size told in
	/// preceding inUploadBegin. Also, inUploadChunk is guaranteed to
	/// be not called before inUploadBegin and after
	/// inUploadOk/inUploadCancelled.
	virtual void inUploadChunk (const HtMdConstChunkDesc &chunk) = 0;

	/// @brief The uploading has completed. File can be closed.
	/// Is guaranteed to be called only after correspponding
	/// inUploadBegin, and only if the client uploaded exactly the
	/// claimed number of bytes.
	virtual void inUploadOk () = 0;

	/// @brief The uploading was cancelled for whatever reason
	/// (including client side cancel, use of cancel method and loss,
	/// disconnection). Partially stored file should be removed from
	/// the storage as a failed upload.
	/// Is guaranteed to be called only after corresponding
	/// inUploadBegin.
	virtual void inUploadCancelled () = 0;

public:
	/// @brief Cancel the uploading. Does nothing if the uploading is
	/// not in progress.
	/// Your implementation must call this function from the destructor
	/// (it's ok to call it multiple times).
	void cancel ();

	/// @brief Checks if the upload is in progress. Sets to true on
	/// request for upload (see beginRequestUpload), gets back to false
	/// on upload success or failure. After you started the upload,
	/// use isInProgress to check if it finished, and then isFailed
	/// (see below) to check if it succeeded.
	bool isInProgress ();

	/// @brief Is set to false if the uploading ended successfully or
	/// to true if it was cancelled.
	/// The return value only is meaningful if isInProgress returns
	/// false. After you started the upload, state of success can be
	/// checked by !isInProgress () && !isFailed (), state of failure
	/// can be checked by !isInProgress () && isFailed ().
	bool isFailed ();
};

/// @brief HtMd file downloader (from application to client). You
/// should derive a class from HtMdFileUploader that implements reading
/// data to send from what is considered a file in your particular
/// implementation.
/// An instance of HtMdFileDownloader incapsulates a session of
/// downloading a single file (request to the client, confirmation from
/// the client and the actual data). The downloader instance is assumed
/// reusable, but only can handle one request at a time.
class HtMdFileDownloader
{
	friend class HtMdSupervisor;
	friend class HtMdLLCFileDownload;
	friend class HtMdLLCFileChunk;

	enum
	{
		ST_OK,
		ST_CANCELLED,
		ST_CLIENT_CANCEL_IN_PROGRESS,
		ST_QUEUED,
		ST_WAITING_ACK,
		ST_WAITING_DATA
	} mState;
	int mDataTotal,mDataSofar;
	std::string mSuggName;

protected:
	HtMdFileDownloader ();
	virtual ~HtMdFileDownloader ();

	/// @brief Request beginning of a download, recommend name/filter
	/// for the file to. Using it while this download is already
	/// in progress cancels current download automatically.
	/// Your implementation is assumed provide its own function to
	/// begin the upload according to specifics of the situation
	/// (open a file for reading, acquire a buffer for downloading
	/// from memory, etc.), which in turn must internally call
	/// beginRequestDownload to begin HtMd part of the work.
	/// @param nameAndFilter Name and (optionally) filters, in
	/// format "filename;filters"  where filters are given in Qt
	/// filters format. The filename will be set as default filename
	/// in client's save file dialog and filters will specify filters
	/// in this dialog.
	/// @param size Size of the file. Exactly as many bytes will be
	/// downloaded to the client.
	void beginRequestDownload
		(const std::string &nameAndFilter,int size);

	/// @brief Client has confirmed beginning of the download. Once
	/// called, is guaranteed not to be called again until next
	/// inDownloadOk/inDownloadCancelled.
	/// @param selectedName Name of the file in client's file system
	/// that user has selected for downloading (without path)
	virtual void inDownloadBegin (const std::string &selectedName) = 0;

	/// @brief Provide next chunk of the downloaded file. Total size
	/// of file chunks are guaranteed to not exceed size told in
	/// preceding inDownloadBegin. Also, inDownloadChunk is guaranteed
	/// to be not called before inDownloadBegin and after
	/// inDownloadOk/inDownloadCancelled.
	virtual void inDownloadChunk (HtMdChunkDesc chunk) = 0;

	/// @brief The downloading has completed. File can be closed.
	/// Is guaranteed to be called only after correspponding
	/// inDownloadBegin, and only if the client uploaded exactly the
	/// claimed number of bytes.
	virtual void inDownloadOk () = 0;

	/// @brief The downloading was cancelled for whatever reason
	/// (including client side cancel, use of cancel method and loss,
	/// disconnection). Resources servicing the download can be
	/// released, the download should be treated as failed.
	/// Is guaranteed to be called only after corresponding
	/// inDownloadBegin.
	virtual void inDownloadCancelled () = 0;

public:
	/// @brief Cancel the downloading. Does nothing if the downloading
	/// is not in progress.
	/// Your implementation must call this function from the destructor
	/// (it's ok to call it multiple times).
	void cancel ();

	/// @brief Checks if the download is in progress. Sets to true on
	/// request for download (see beginRequestDownload), gets back to
	/// false on download success or failure. After you started the
	/// download, use isInProgress to check if it finished, and then
	/// isFailed (see below) to check if it succeeded.
	bool isInProgress ();

	/// @brief Is set to false if the downloading ended successfully or
	/// to true if it was cancelled.
	/// The return value only is meaningful if isInProgress returns
	/// false. After you started the download, state of success can be
	/// checked by !isInProgress () && !isFailed (), state of failure
	/// can be checked by !isInProgress () && isFailed ().
	bool isFailed ();
};


/// @brief HtMd supervisor. A singleton. Create one single instance of
/// this object per lifetime of your program, then call update ()
/// periodically (every frame for example) and exploit HtMd via
/// HtMdUnit-s, HtMdFileUploader-s and HtMdFileDownloader-s.
class HtMdSupervisor
{
	friend class HtMdUnit;
	friend class HtMdFileUploader;
	friend class HtMdFileDownloader;
	friend class HtMdLLCAppCmdBegin;
	friend class HtMdLLCAppCmdChunk;
	friend class HtMdLLCAppCmdCancel;
	friend class HtMdLLCFileChunk;

	static HtMdSupervisor *spInstance;
	HtMdConnection &mConnection;

	char mInChunkLen[4],
		mInChunkData[HTMD_MAX_PACKET_SIZE];
	unsigned mInChunkLenSofar,mInChunkDataSofar,mInChunkDataTotal;

	char mOutChunkLen[4],
		mOutChunkData[HTMD_MAX_PACKET_SIZE];
	unsigned mOutChunkLenSofar,mOutChunkDataSofar,mOutChunkDataTotal;

	bool mInChunkInProgress,mOutChunkInProgress,
		mOutChunkPrepared; // prepared to be sent

	void handleInputLLC ();
	void prepareMoreOutput ();

	int mNextUnitId;
	std::map<int,HtMdUnit *> mUnitsById;

	// queue of outgoing commands
	std::deque<HtMdLowLevelAppCmd *> mOutLLCs;
	std::set<HtMdUnit *> mNewUnits;
	std::set<HtMdUnit *> mIdleSenderUnits;
	// ^units that try to send commands while unconnected,
	// such commands must be treated as cancelled ASAP

	void dropUnitOutCommands (HtMdUnit *);
	void dropAllOutCommands ();
	HtMdUnit *getUnitById (int unitId);

	std::set<HtMdFileUploader *> mUploaders;
	std::deque<HtMdFileUploader *> mUploadRequestQueue;
	HtMdFileUploader *mpCurrentUploader;

	void dropUploaderOutCommands (HtMdFileUploader *);

	std::set<HtMdFileDownloader *> mDownloaders;
	std::deque<HtMdFileDownloader *> mDownloadRequestQueue;
	HtMdFileDownloader *mpCurrentDownloader;
	int mDownloadFilePacketsPending;

	void dropDownloaderOutCommands (HtMdFileDownloader *);

public:
	/// @brief Construct supervisor instance (only one instance per
	/// program is allowed). You need to have a supporting instance
	/// of HtMdConnection derived class and specify it in the ctor.
	/// The connection object must exist throughout whole lifetime
	/// of the WBSVSupervisor.
	HtMdSupervisor (HtMdConnection &);
	~HtMdSupervisor ();

	/// @brief Update HtMd state. Call this function once in a while
	/// to progress HtMd input/output and other operations.
	static void update ();

	/// @brief Return true if client console is connected, false
	/// otherwise.
	static bool isConnected ();

	/// @brief Return true if the supervisor is defunct and can no
	/// longer be used, false otherwise
	static bool isDefunct ();

	/// @brief Declare an instance of this class as a local variable
	/// in a scope where you want to have a critical section for
	/// accessing HtMd functions.
	///
	/// All HtMdSupervisor, HtMdUnit, HtMdFileUploader and
	/// HtMdFileDownloader methods, including insides of virtual
	/// callbacks, already use this lock internally and therefore
	/// are inherently thread safe without your explicit efforts.
	/// However, you will need to make use of this class in
	/// implementation specific methods of your own units,
	/// uploaders and downloaders if you want to make them
	/// thread safe as well. In particular, you will need it in
	/// destructors and helper classes/functions (e. g. smart
	/// pointers) that imply thread-shared access to your HtMd
	/// objects.
	class Lock
	{
	public:
		Lock ();
		~Lock ();
	};
};

#endif // HTMD_DISABLED

#endif // HTMD_H_INCLUDED
